﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using AdminModule.IndexMarkServiceReference;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using SharedCode;
using AdminModule.Views;
using InpValidator;
using AdminModule.StructEditStratagies;
using AdminModule.CommonClasses;
using AdminModule.CustomXAMLElements;
using System.Reflection;
using AdminModule.ViewModels;

namespace AdminModule.UserControls.StructView
{
    public enum StructName { IndexMark, University, Gradations };

    public partial class StructViewBase : UserControl
    {
        // Загрузка по требованию
        private bool _isLoadOnDemand = true;
        // Делаем свойством, чтобы обратиться из XAML
        public bool isLoadOnDemand
        {
            get { return this._isLoadOnDemand; }
            set { this._isLoadOnDemand = value; }
        }

        protected int IDTerm;
        private ItemsBuffer currentItem;
        private ItemsBuffer bufferItem;
        private StructEditBaseStrategy _structEditStrategy;

        protected CommonVM2 viewModel;

        // Коллекция измененных объектов (СТАТИЧЕСКАЯ, ЧТОБЫ НЕ ТЕРЯТЬ МОДИФИЦИРОВАННЫЕ ОБЪЕКТЫ ПРИ ПЕРЕХОДЕ В ДРУГОЙ РАЗДЕЛ АДМИНИСТРИРОВАНИЯ)
        public static readonly ObservableCollection<DTOBase> changedElements = new ObservableCollection<DTOBase>();

        /// <summary>
        /// Событие срабатывает, когда пользователь выбирает один из узлов дерева
        /// </summary>
        public event CurrentItemChangedHandler CurrentItemChanged;
        public delegate void CurrentItemChangedHandler(ItemsBuffer old, ItemsBuffer _new);

        public readonly EventsContainer events = new EventsContainer();

        public StructViewBase()
        {
            InitializeComponent();
            this.structTreeView.SelectedItemChanged +=new RoutedPropertyChangedEventHandler<object>(structTreeView_SelectedItemChanged);
            // Подписываемся на событие о выполненном сохранении, чтобы очистить коллекцию модифицированных элементов
            this.events.SaveCompleted += new EventsContainer.SaveCompletedHandler(StructViewBase_SaveCompleted);
            this.events.CopyCompleted += new EventsContainer.CopyCompletedHandler(StructViewBase_CopyCompleted);
            this.TreeView.NodeExpandedOnce += new RoutedEventHandler(structTreeView_NodeExpanded);
        }

        #region Подписчики
        // Подписываемся на событие, чтобы очистить коллекцию модифицированных элементов
        private void StructViewBase_SaveCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                StructViewBase.changedElements.Clear();
            }
        }

        #region Сброс состояния объектов
        private void ResetStates()
        {
            BindingExpression be = structTreeView.GetBindingExpression(MyTreeView.ItemsSourceProperty);
            PropertyInfo pi = typeof(CommonVM2).GetProperty(be.ParentBinding.Path.Path);
            ResetStatesRecursive(pi.GetValue(be.DataItem, null) as IEnumerable<DTOBase>);
        }

        private void ResetStatesRecursive(IEnumerable<DTOBase> from)
        {
            foreach (DTOBase el in from)
            {
                el.State = StateObject.Unchanged;
                if (el is DTOChilds) ResetStatesRecursive((el as DTOChilds).Childs);
            }
        }
        #endregion

        void StructViewBase_CopyCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                StructViewBase.changedElements.Clear();
            }
        }

        private void structTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (e.NewValue == null) return;

            SetCurrentItem(e.NewValue);

            // Подписываемся на событие PropertyChanged, чтобы определить редактирование элемента
            DTOBase obj = e.NewValue as DTOBase;
            // Если это не только что добавленный, удаленный или перемещенный элемент, значит выполняется редактирование
            if (obj.State == StateObject.Unchanged)
            {
                obj.PropertyChanged += (s, ev) =>
                {
                    // Childs2 - это свойство, применяемое только для загрузки по требованию
                    if (ev.PropertyName != "Childs2" && ev.PropertyName != "State")
                    {
                        structEditStrategy.Edit(obj);
                    }
                };
            }
        }

        private void SetCurrentItem(object NewValue)
        {
            ItemsBuffer oldCurrentItem = currentItem;
            currentItem = new ItemsBuffer
            {
                parentItem = this.TreeView.GetParentItem(NewValue) as DTOBase,
                selectedItem = NewValue as DTOBase
            };
            this.RaiseCurrentItemChanged(oldCurrentItem, currentItem);
        }
        #endregion

        #region Свойства
        public ItemsBuffer CurrentItem
        {
            get
            {
                return this.currentItem;
            }
        }

        public ItemsBuffer BufferItem
        {
            get
            {
                return this.bufferItem;
            }
        }

        public ValidatorManager ValidatorManager
        {
            get
            {
                return this.MainManager;
            }
        }

        public MyTreeView TreeView
        {
            get
            {
                return this.structTreeView;
            }
        }

        public CommonVM2 ViewModel
        {
            get
            {
                return this.viewModel;
            }
        }

        public StructEditBaseStrategy structEditStrategy
        {
            get
            {
                return this._structEditStrategy;
            }
            set
            {
                this._structEditStrategy = value;
                this._structEditStrategy.ObjectChangedEvent += (obj) =>
                {
                    if (!changedElements.Contains(obj))
                    {
                        changedElements.Add(obj);
                    }
                };
            }
        }
        #endregion

        #region Матоды вызова событий
        private void RaiseCurrentItemChanged(ItemsBuffer old, ItemsBuffer _new)
        {
            if (CurrentItemChanged != null)
            {
                CurrentItemChanged(old, _new);
            }
        }
        #endregion

        public void Add(DTOBase newObj) 
        {
            if (structEditStrategy.Add(newObj, currentItem.selectedItem))
            {
                TreeViewItem item = TreeView.GetSelectedContainer();
                //item.IsExpanded = true;
            }
        }

        public void Delete() 
        {
            structEditStrategy.Delete(currentItem.selectedItem, currentItem.parentItem);
        }

        public void Cut()
        {
            bufferItem = currentItem;
        }

        public void Paste()
        {
            // Если перемещение элементов выполнено успешно, очищаем буфер
            if (structEditStrategy.Move(bufferItem.selectedItem, bufferItem.parentItem, currentItem.selectedItem))
            {
                bufferItem = null;
                TreeViewItem item = TreeView.GetSelectedContainer();
                //item.IsExpanded = true;
            }
        }

        public void Replace(DTOBase newObj)
        {
            structEditStrategy.Replace(newObj, currentItem.selectedItem, currentItem.parentItem);
        }

        public void SetTemplate(DataTemplate template)
        {
            if (this.structTreeView.ItemTemplate == null)
            {
                this.structTreeView.ItemTemplate = template;
            }
        }

        public void SetBinding(object source, string propertyName)
        {
            Binding b = new Binding(propertyName);
            b.Source = source;
            b.Mode = BindingMode.TwoWay;
            this.structTreeView.SetBinding(MyTreeView.ItemsSourceProperty, b);
        }

        #region Вирутальные методы
        /// <summary>
        /// Вызов базового метода обязятелен
        /// </summary>
        /// <param name="IDTerm"></param>
        public virtual void Init(int IDTerm) 
        {
            this.IDTerm = IDTerm;
        }

        public virtual void LoadData() 
        {
            throw new NotImplementedException();
        }

        public virtual void LoadOnDemand(TreeViewItem treeViewItem, DTOBase obj) 
        {
            throw new NotImplementedException();
        }

        public virtual void Save() 
        {
            throw new NotImplementedException();
        }

        public virtual void CopyAll(int fromIDTerm, int toIDTerm) 
        {
            throw new NotImplementedException();
        }

        public virtual void CopySelected(DTOBase targetObject, int toIDTerm) 
        {
            throw new NotImplementedException();
        }

        public virtual List<TypeInfo> GetTypesList() 
        {
            throw new NotImplementedException();
        }
        #endregion

        private void structTreeView_NodeExpanded(object sender, RoutedEventArgs e)
        {
            // Если пользователь раскрывает узел - вызывается метод загрузки данных по требованию
            MyTreeViewItem item = sender as MyTreeViewItem;
            DTOBase currentItem = item.GetItem() as DTOBase;

            if ((currentItem.State == StateObject.Unchanged || currentItem.State == StateObject.Updated) && isLoadOnDemand)
            {
                this.LoadOnDemand(item, currentItem);
            }
        }

        public void RaiseLoadOnDemand()
        {
            TreeView.RaiseNodeExpanded(TreeView.GetSelectedContainer() as MyTreeViewItem);
        }
    }

    public class ItemsBuffer
    {
        public DTOBase parentItem { get; set; }
        public DTOBase selectedItem { get; set; }
    }
}
